package com.lagou.edu.factory;

import com.lagou.edu.annotation.MyAutowired;
import com.lagou.edu.annotation.MyRepository;
import com.lagou.edu.annotation.MyService;
import com.lagou.edu.annotation.MyTransactional;
import com.lagou.edu.utils.ConnnectioUtils;
import com.lagou.edu.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

public class BeanFactory {

    static Map<String, Object> beans = new HashMap<>();
    static List<Class> clazzNames = new ArrayList<>();

    static {
        //获取包路径下所有类
        try {
            loacClazzNames("com.lagou.edu");
            //通过反射创建对象
            createObjects();
            //创建代理
            createProxyObjexts();
            //属性填充
            propertySet();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Bean 的属性填充
     * @throws Exception
     */
    private static void propertySet() throws Exception {
        Collection<Object> values = beans.values();
        for (Object value : values) {
            Field[] fields = value.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if(field.isAnnotationPresent(MyAutowired.class)) {
                    String fieldName = field.getName();
                    Object fileValue = beans.get(fieldName);
                    field.set(value,fileValue);
                }
            }
        }
    }

    /**
     * 为对象创建代理
     */
    private static void createProxyObjexts() {
        if(clazzNames.isEmpty()){
            for (int i = 0; i < clazzNames.size(); i++) {
                Class aClass = clazzNames.get(i);
                if(aClass.isAnnotationPresent(MyTransactional.class)){
                    //判断是否实现接口
                   if(aClass.isInterface()){
                       //jdk动态代理
                      Object jdkProxyObj= crateJdkProxy(aClass);
                      beans.put(aClass.getSimpleName(),jdkProxyObj);
                   }else {
                       //使用cglib 进行代理
                       Object cglibProxyObj = crateCgLibProxy(aClass);
                       beans.put(aClass.getSimpleName(),cglibProxyObj);
                   }

                }
            }
        }
    }

    /**
     * 创建cglib代理
     * @param aClass
     * @return
     */
    private static Object crateCgLibProxy(Class aClass) {
        Enhancer enhancer = new Enhancer();
        //继承被代理类
        enhancer.setSuperclass(aClass);
        //设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object ret = null;
                ConnnectioUtils connnectioUtils = ConnnectioUtils.getInstance();
                TransactionManager transactionManager = TransactionManager.getTransactionManager();
                transactionManager.setConnnectioUtils(connnectioUtils);
                try {
                    transactionManager.beginTransaction();
                    ret= method.invoke(o,objects);
                    transactionManager.commit();

                }catch (Exception e){
                    transactionManager.rollback();
                }
                return  ret;
            }
        });

        return enhancer.create();
    }

    /**
     * 创建jdk代理
     * @param aClass
     * @return
     */
    private static Object crateJdkProxy(Class aClass) {
        return Proxy.newProxyInstance(BeanFactory.class.getClassLoader(), aClass.getInterfaces(), new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object ret=null;
                String simpleName = aClass.getSimpleName();
                Object obj = beans.get(simpleName);
                    //创建事务管理器
                ConnnectioUtils connnectioUtils = ConnnectioUtils.getInstance();
                TransactionManager transactionManager = TransactionManager.getTransactionManager();
                transactionManager.setConnnectioUtils(connnectioUtils);
                try {
                    //开启事务
                    transactionManager.beginTransaction();
                    ret=method.invoke(obj,args);
                    //提交事务
                    transactionManager.commit();
                }catch (Exception e){
                    transactionManager.rollback();
                }
                return ret;
            }
        });
    }

    /**
     * 创建对象
     * @throws Exception
     */
    private static void createObjects() throws Exception {
        if (clazzNames.isEmpty()) {
            for (int i = 0; i < clazzNames.size(); i++) {
                //获取添加了MyService | MyRepostitory 的class
                Class aClass = clazzNames.get(i);
                if (aClass.isAnnotationPresent(MyService.class) ||
                        aClass.isAnnotationPresent(MyRepository.class)) {
                    String simpleName = aClass.getSimpleName();
                    Object obj = aClass.newInstance();
                    beans.put(simpleName, obj);
                }
            }
        }

    }

    /**
     * 根据包路径 加载类路径
     * @param packs
     * @throws Exception
     */
    private static void loacClazzNames(String packs) throws Exception {
        //将page路径替换成文件路径
        String replacePack = packs.replace(".", "/");
        URL resource = BeanFactory.class.getClass().getClassLoader().getResource(replacePack);
        URI uri = resource.toURI();
        File file = new File(uri);
        scanClasses(file, replacePack);

    }

    /**
     * 对象的扫描
     * @param file
     * @param packageStr
     * @throws Exception
     */
    private static void scanClasses(File file, String packageStr) throws Exception {
        File[] files = file.listFiles();
        for (int i = 0; i < files.length; i++) {
            File f = files[i];
            if (f.isDirectory()) {
                scanClasses(f, packageStr);
            } else {
                String classzzName = f.getName();
                String targClassName = classzzName.replace(".class", "");
                Class<?> aClass = Class.forName(packageStr + "." + targClassName);
                clazzNames.add(aClass);
            }
        }

    }


    //    static {
//        //读取xml文件流
//        InputStream beansStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//        //dom4j解析xml
//       SAXReader reader = new SAXReader();
//        try {
//            Document read = reader.read(beansStream);
//            Element rootElement = read.getRootElement();
//            List<Element> list = rootElement.selectNodes("//bean");
//            for (int i = 0; i < list.size(); i++) {
//                Element element = list.get(i);
//                String id = element.attributeValue("id");
//                //读取class属性的类路径
//                String aClass = element.attributeValue("class");
//                //实例化对象
//                Class<?> clazz = Class.forName(aClass);
//                Object obj = clazz.newInstance();
//                //存入map
//                beans.put("id",obj);
//            }
//            //解析property标签
//            List<Element> propertys = rootElement.selectNodes("//property");
//            for (int i = 0; i < propertys.size(); i++) {
//                Element element = propertys.get(i);
//
//                String ref = element.attributeValue("ref");
//                //获根据ref属性获取要注入的对象
//                Object obj = beans.get(ref);
//                //注入方法名
//                String setName = element.attributeValue("name");
//                //获取要注入的对象
//                String parentId = element.getParent().attributeValue("id");
//                Object parentObj = beans.get(parentId);
//                Method method = parentObj.getClass().getMethod("set" + setName, obj.getClass());
//                method.invoke(parentObj,obj);
//                beans.put(parentId,parentObj);
//            }
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
    public static Object getBean(String id) {
        return beans.get(id);
    }
}
